package com.ruoyi.common.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultClock;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * @author anan.
 * @version 1.0
 * @create 2021-06-16 14-28
 */
@Component
@Data
public class JwtTokenUtil implements Serializable {
	private static final long serialVersionUID = -3301605591108950415L;

	@Value("${jwt.secret}")
	private  String secret;

	@Value("${jwt.expiration}")
	private Long expiration;

	@Value("${jwt.token}")
	private String tokenHeader;

	private Clock clock = DefaultClock.INSTANCE;

	/**
	 * 根据UserDetails中的用户名 生成token
	 * @param userDetails
	 * @return
	 */
	public String generateToken(UserDetails userDetails) {
		Map<String, Object> claims = new HashMap<>(16);
		return doGenerateToken(claims, userDetails.getUsername());
	}

	/**
	 * 根据用户名 生成token
	 * @param
	 * @return
	 */
	public String generateToken(String username) {
		Map<String, Object> claims = new HashMap<>(16);
		return doGenerateToken(claims, username);
	}

	/**
	 * 生成token密钥
	 * @param claims
	 * @param subject
	 * @return
	 */
	private String doGenerateToken(Map<String, Object> claims, String subject) {
		final Date createdDate = clock.now();
		final Date expirationDate = calculateExpirationDate(createdDate);

		return Jwts.builder()
			.setClaims(claims)
			.setSubject(subject)
			.setIssuedAt(createdDate)
			.setExpiration(expirationDate)
			.signWith(SignatureAlgorithm.HS512, secret)
			.compact();
	}

	/**
	 * 设置过期时间 expiration 为token时间长度 从application配置文件中获取
	 * @param createdDate
	 * @return
	 */
	private Date calculateExpirationDate(Date createdDate) {
		return new Date(createdDate.getTime() + expiration);
	}

	/**
	 * 从前端传回来的TOKEN与系统中的userDetails进行对比 返回true false
	 * @param token 上下文获取的token
	 * @param userDetails 数据库获取的用户信息
	 * @return
	 */
	public Boolean validateToken(String token, UserDetails userDetails) {
		SecurityUserDetails user = (SecurityUserDetails) userDetails;
		final String username = getUsernameFromToken(token);
		return (username.equals(user.getUsername()) && !isTokenExpired(token)
		);
	}

	/**
	 * 从token中获取 username
	 * @param token
	 * @return
	 */
	public String getUsernameFromToken(String token) {
		return getClaimFromToken(token, Claims::getSubject);
	}

	/**
	 * 从token中获取 claim类型数据
	 * @param token
	 * @param claimsResolver
	 * @param <T>
	 * @return
	 */
	public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
		final Claims claims = getAllClaimsFromToken(token);
		return claimsResolver.apply(claims);
	}

	/**
	 * 从JWT中解析token的信息
	 * @param token
	 * @return
	 */
	private Claims getAllClaimsFromToken(String token) {
		return Jwts.parser()
			.setSigningKey(secret)
			.parseClaimsJws(token)
			.getBody();
	}

	/**
	 * 判断token是否是否过期
	 * @param token
	 * @return
	 */
	private Boolean isTokenExpired(String token) {
		final Date expiration = getExpirationDateFromToken(token);
		return expiration.before(clock.now());
	}

	/**
	 * 获取token的过期时间
	 * @param token
	 * @return
	 */
	public Date getExpirationDateFromToken(String token) {
		return getClaimFromToken(token, Claims::getExpiration);
	}

}
